
import 'package:flutter/material.dart';
import 'package:todo_app/constants/Screen.dart';
import 'CalendarController.dart';
import 'CalendarWeekday.dart';
import 'CalendarScrollPhysics.dart';
import 'CalendarUtil.dart';

typedef Widget DayWidgetBuilder(DateTime date, bool isLastMonthDay, bool isNextMonthDay);

typedef void CurrentPageDate(DateTime dateTime);

class CalendarCarousel extends StatefulWidget {

  final DateTime startTime;  // 启始日期
  final DateTime endTime;  // 结束日期
  final DateTime currentPageTime;  // 当前要显示的日期, 默认当前时间

  final DateTime? centerTime; // 收缩的时候想要显示在中心的时间,默认是这个月1日

  final CurrentPageDate? currentPageDate;

  final CalendarWeekday weekdayHeader;
  final DayWidgetBuilder dayWidgetBuilder;
  final double childAspectRatio;

  final CalendarShow calendarShow;
  final CalendarController? calendarController;

  final double shrinkageHeight;  // 需要收缩的高度

  CalendarCarousel({
    Key? key,
    required this.startTime,
    required this.endTime,
    required this.weekdayHeader,

    this.currentPageDate,
    required this.dayWidgetBuilder,
    this.childAspectRatio = 1,
    this.calendarShow = CalendarShow.calendarShowMonth,
    this.calendarController,
    this.shrinkageHeight = 1,
    this.centerTime,

    currentPageTime,
    pageController,
  }) : 
        currentPageTime = currentPageTime ?? DateTime.now(),
        assert(!(currentPageTime.isBefore(startTime))),
        assert(!(endTime.isBefore(startTime))),
        assert(!(endTime.isBefore(currentPageTime))),
        super(key: key);

  @override
  _CalendarCarouselState createState() => _CalendarCarouselState();
}

class _CalendarCarouselState extends State<CalendarCarousel> with TickerProviderStateMixin {
  late PageController _pageController;
  late CalendarScrollPhysics _scrollPhysics;
  late ScrollController _monthController;
  late CalendarUtil  _calendarUtil;
  late CalendarController _calendarController;
  double _aspectRatio = 1;
  double _monthRatio = 1;
  double _weekRatio = 1;

  @override
  void initState() {
    _calendarUtil = CalendarUtil(
      initTime: widget.currentPageTime,
      firstDayOfWeek: widget.weekdayHeader.firstDayOfWeek,
      childAspectRatio: widget.childAspectRatio,
      calendarShow: widget.calendarShow,
    );
    _pageController = PageController(
      viewportFraction: 1,
      initialPage: _calendarUtil.initIndex(),
      keepPage: false,
    );

    _monthController = ScrollController(
      keepScrollOffset: true,
    );

    _calendarController = widget.calendarController ?? CalendarController();
    _calendarController.pageController = _pageController;
    _calendarController.calendarUtil = _calendarUtil;
    _calendarController.onChangeCalendarShow = _changeCalendarShow;
    _calendarController.onUpdateOffset = _updateOffset;

    _scrollPhysics = CalendarScrollPhysics(Status(), _calendarUtil);

    _calendarController.scrollPhysics = _scrollPhysics;

    _monthRatio = _calendarUtil.getAspectRatio(widget.currentPageTime, show: CalendarShow.calendarShowMonth);
    _weekRatio = _calendarUtil.getAspectRatio(widget.currentPageTime, show: CalendarShow.calendarShowWeek);
    _aspectRatio =  _calendarUtil.calendarShow == CalendarShow.calendarShowMonth ? _monthRatio : _weekRatio;

    super.initState();


    // 修改最大最小范围
    _setScrollPhysicMaxMin();
  }

  @override
  void dispose() {
    super.dispose();
    _pageController.dispose();
    _monthController.dispose();
    _calendarController.dispose();
  }

  _changeCalendarShow(CalendarShow calendarShow, DateTime? dateTime) {
    Future.delayed(Duration.zero, () {
      var date = dateTime ?? widget.currentPageTime;
      _calendarUtil.calendarShow = calendarShow;

      var currentPage = _calendarUtil.indexOf(date);
      _pageController.jumpToPage(currentPage);
      _aspectRatio =  _calendarUtil.calendarShow == CalendarShow.calendarShowMonth ? _monthRatio : _calendarUtil.getAspectRatio(widget.currentPageTime, show: CalendarShow.calendarShowWeek);
      _setScrollPhysicMaxMin();
      _updateOffset();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Offstage(
          offstage: widget.weekdayHeader.builder == null,
          child: _weekDayWidget(),
        ),
        _calendarDayWidget(),
      ],
    );
  }

  Widget _calendarDayWidget() {
    double shrinRatio = Screen.width / (Screen.width / _monthRatio - widget.shrinkageHeight);
    shrinRatio = double.parse((shrinRatio).toStringAsFixed(14));
    // 更新ziview的位移
    _updateOffset();

    if( _calendarUtil.calendarShow == CalendarShow.calendarShowMonth) {
      _aspectRatio = shrinRatio;
    } else {
      _aspectRatio = _weekRatio;
    }

    return AspectRatio(
      aspectRatio: _aspectRatio, //  MediaQuery.of(context).size.width / _aspectRatio
      child: PageView.builder(
        onPageChanged: _pageChanged,
        controller: _pageController,
        physics: _scrollPhysics,
        itemBuilder:(BuildContext context,int index) {
          if(_calendarUtil.calendarShow == CalendarShow.calendarShowMonth) {
            return _monthView(index);
          } else {
            return _weekView(index);
          }
        },
        scrollDirection: Axis.horizontal,
      ),
    );
  }


  Widget _monthView(int index) {

    DateTime time =  _calendarUtil.getActualDate(index, widget.currentPageTime);
    if(_calendarUtil.calendarShow == CalendarShow.calendarShowWeek) {
      time = DateTime(widget.currentPageTime.year, widget.currentPageTime.month, 1);
    }

    int year = time.year;
    int month = time.month;
    var firstWeekDay = DateTime(year, month, 1).weekday % 7;
    var lastMonthRestDayCount = (firstWeekDay - (widget.weekdayHeader.firstDayOfWeek % 7)) % 7;

    var thisMonthDayCount = DateTime(year, month + 1, 0).day;
    var lastMonthDayCount = DateTime(year, month, 0).day;
    /// 行数
    var rowCount = ((thisMonthDayCount + lastMonthRestDayCount) / 7.0).ceil();

    return Container(
      width: double.infinity,
      color: Colors.transparent,
      child: GridView.builder(
        padding: EdgeInsets.zero,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 7,
          childAspectRatio: widget.childAspectRatio,
        ),
        itemCount: rowCount * 7,
        itemBuilder: (BuildContext context, int index) {
          var currentDay = index + 1 - lastMonthRestDayCount;
          // last month day
          var isLastMonthDay = false;
          // next month day
          var isNextMonthDay = false;

          var currentMonth = month;
          if (currentDay <= 0) {
            isLastMonthDay = true;
            currentDay = lastMonthDayCount + currentDay;
            currentMonth -= 1;
          } else if (currentDay > thisMonthDayCount) {
            isNextMonthDay = true;
            currentDay = currentDay - thisMonthDayCount;

            currentMonth += 1;
          }
          //
          return widget.dayWidgetBuilder(DateTime(year, currentMonth, currentDay), isLastMonthDay, isNextMonthDay);
        },

        controller: _monthController,
        // physics: NeverScrollableScrollPhysics(),
      ),
    );
  }

  Widget _weekView(int index) {

    DateTime currentDate =  _calendarUtil.getActualDate(index, widget.currentPageTime);

    return Container(
      width: double.infinity,
      color: Colors.transparent,
      child: GridView.count(
        crossAxisCount: 7,
        childAspectRatio: widget.childAspectRatio,
        children: List.generate(7, (index) {
          // 左边的日期个数
          var leftCount = (currentDate.weekday % 7) - (widget.weekdayHeader.firstDayOfWeek % 7);

          var date = currentDate.add(Duration(days: index - leftCount));

          // last month day
          var isLastMonthDay = date.month < currentDate.month;
          // next month day
          var isNextMonthDay = date.month > currentDate.month;
          return widget.dayWidgetBuilder(date, isLastMonthDay, isNextMonthDay);
        }),
      ),
    );
  }


  _pageChanged(int index) {
    DateTime currentTime = widget.currentPageTime;
    if(_calendarController.currentTime != null) {
      currentTime = _calendarController.currentTime!;
      _calendarController.currentTime = null;
    }
    DateTime currentPageTime  = _calendarUtil.getActualDate(index, currentTime);

    if(currentPageTime.isBefore(widget.startTime)) {
      currentPageTime = widget.startTime;
    }

    _monthRatio = _calendarUtil.getAspectRatio(currentPageTime, show: CalendarShow.calendarShowMonth);
    _weekRatio = _calendarUtil.getAspectRatio(currentPageTime, show: CalendarShow.calendarShowWeek);
    _aspectRatio =  _calendarUtil.calendarShow == CalendarShow.calendarShowMonth ? _monthRatio : _weekRatio;
    if(widget.currentPageDate != null) {
      widget.currentPageDate!(currentPageTime);
    }
  }

  _setScrollPhysicMaxMin() {
    _scrollPhysics.status.minScrollExtent = Screen.width * _calendarUtil.indexOf(widget.startTime);
    _scrollPhysics.status.maxScrollExtent = Screen.width * _calendarUtil.indexOf(widget.endTime);
  }

  List<Widget> _renderWeekDays() {
    var list = [];
    for (int i = 0; i < 7; i++) {
      var weekday = (widget.weekdayHeader.firstDayOfWeek + i) % 7;
      if (weekday == 0) weekday = 7;
      list.add(weekday);
    }
    return list.map((i) => widget.weekdayHeader.builder!(i)).toList();
  }

  Widget _weekDayWidget(){
    return Container(
      width: double.infinity,
      height: widget.weekdayHeader.height,
      color: Colors.transparent,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: _renderWeekDays(),
      ),
    );
  }

  void _updateOffset(){
    if(widget.shrinkageHeight == 0 || _calendarUtil.calendarShow == CalendarShow.calendarShowWeek) return;
    // 选中的日期在第几行，
    var currentRow = CalendarUtil.getCurrentRow(widget.weekdayHeader.firstDayOfWeek, widget.currentPageTime);
    // 一共有几行
    var rowCount = CalendarUtil.getRowCount(widget.currentPageTime.year, widget.currentPageTime.month, widget.weekdayHeader.firstDayOfWeek);
    // 上面几行,上面占用百分比
    double persent = (currentRow-1.0)/(rowCount-1.0);
    double offset = persent*widget.shrinkageHeight;
    if(_monthController.hasClients) {
      _monthController.jumpTo(offset);
    }
  }
}
